/*
Filename:    circular_buffer_const_iterator.h
Author:      Erik Öjebo
Date:        2009-01-10
Description: A generic circular buffer, with a custom iterator class
             (CircularBufferConstIterator).
Note:        The implementation is in the header file to avoid problems,
             since the class is generic.
*/

#ifndef CIRCULAR_BUFFER_H_
#define CIRCULAR_BUFFER_H_

#include <cstdlib>
#include "circular_buffer_const_iterator.h"

template<class T>
class CircularBuffer
{
public:
    // Creates a circular buffer of size [bufferSize].
    CircularBuffer(size_t bufferSize);

    // Deallocates dynamically allocated memory.
    ~CircularBuffer();

    // Adds an element to the end of the buffer.
    void pushBack(T& data);

    // Returns an iterator pointing to the first element of the buffer.
    CircularBufferConstIterator<T> begin() const;

private:
    // Returns the index of the internal array that currently holds the oldest
    // element.
    int getOldestIndex() const;

    // Returns the index of the internal array that currently holds the newest
    // element.
    int getNewestIndex() const;

    // Returns the index that follows the given index.
    int getNextIndex(int index) const;

    // Returns the data for a specific index.
    const T& data(int index) const;

    friend class CircularBufferConstIterator<T>;
    int endIndex;
    size_t messageSize, bufferSize;
    T* buffer;
};

template<class T>
CircularBuffer<T>::CircularBuffer(size_t bufferSize)
{
    // Initialize state:
    this->bufferSize = bufferSize;
    this->endIndex = bufferSize - 1;

    // Allocate a buffer of bufferSize rows of size messageSize
    buffer = new T[bufferSize];
}

template<class T>
CircularBuffer<T>::~CircularBuffer()
{
    // Free dynamically allocated memory:
    if (buffer != NULL)
    {
        delete buffer;
        buffer = NULL;
    }
}

template<class T>
void CircularBuffer<T>::pushBack(T& data)
{
    // Add the element to the buffer and update the indexes that point to the
    // newest/oldest indices. Only the oldest index is updated since the newest
    // is calculated from the oldest.
    int oldestIndex = getOldestIndex();
    buffer[oldestIndex] = data;
    endIndex = oldestIndex;
}

template<class T>
int CircularBuffer<T>::getOldestIndex() const
{
    // The oldest element is the first element in the buffer:
    return getNextIndex(endIndex);
}

template<class T>
int CircularBuffer<T>::getNewestIndex() const
{
    // The newest element is the last element in the buffer:
    return endIndex;
}

template<class T>
int CircularBuffer<T>::getNextIndex(int index) const
{
    // Return the next index for a given index. Wraps around.
    return (index + 1) % bufferSize;
}

template<class T>
const T& CircularBuffer<T>::data(int index) const
{
    // Return the data for the given index:
    return buffer[index];
}

template<class T>
CircularBufferConstIterator<T> CircularBuffer<T>::begin() const
{
    // Create an iterator for this buffer and return it:
    return CircularBufferConstIterator<T>(this);
}

#endif
